package com.mybook.app.service;

import com.mybook.app.conf.SmsConfig;
import com.mybook.app.conf.TokenConfig;
import com.mybook.app.core.BaseBean;
import com.mybook.app.core.UserContext;
import com.mybook.app.dto.BorrowTimesDto;
import com.mybook.app.error.Error;
import com.mybook.app.error.ErrorCode;
import com.mybook.app.exception.NotExistUserException;
import com.mybook.app.exception.SmsCheckException;
import com.mybook.app.exception.UserInfoException;
import com.mybook.app.exception.VailDataException;
import com.mybook.app.feign.SmsFeignClient;
import com.mybook.app.mapper.*;
import com.mybook.app.model.*;
import com.mybook.app.request.*;
import com.mybook.app.response.SmsFeignResponse;
import com.mybook.app.response.SmsInfo;
import com.mybook.app.response.Token;
import com.mybook.app.response.UserInfoDetail;
import com.mybook.app.utils.*;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.security.MessageDigest;
import java.util.*;

/**
 * Created by zjutsw on 2017/10/27.
 */
@Service
public class UserService {
    @Autowired
    private SmsMapper smsMapper;
    @Autowired
    private SmsConfig smsConfig;
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private UserInfoTokenMapper userInfoTokenMapper;
    @Autowired
    private TokenConfig tokenConfig;
    @Autowired
    private TransInfoMapper transInfoMapper;
    @Autowired
    private SmsFeignClient smsFeignClient;
    @Autowired
    private SendSmsLogMapper sendSmsLogMapper;

    private final static Integer VAILDFLAG = 1;

    /**
     * @param smsRequest
     * @return
     */
    public String sendSms(SmsRequest smsRequest) {
        // 获取验证码校验
        vailSms(smsRequest);
        String uuid = UUIDUtils.getInstance().getUniqueId();
        // 验证码保存数据库，用于验证
        String smsCode = randomNumber(smsConfig.getLength());
        Logger.debug(this, "验证码： " + smsCode);
        //对象封装
        Sms sms = new Sms();
        sms.setFlag(0);
        sms.setMobile(smsRequest.getMobile());
        sms.setcTime(new Date());
        sms.setuTime(new Date());
        sms.setSmsUuid(uuid);
        sms.setRetry(smsConfig.getRetry());
        sms.setSmsCode(smsCode);
        sms.setVaildate(new Date(System.currentTimeMillis() + smsConfig.getVaildate()));
        int row = smsMapper.insert(sms);
        Logger.debug(this, "插入sms成功 ： " + row);
        sendSms(sms.getMobile(), smsCode, uuid);
        return uuid;
    }

    /**
     * 短信发送
     *
     * @param mobile
     * @param smsCode
     */
    private void sendSms(String mobile, String smsCode, String smsUuid) {
        SendSmsLog sendSmsLog = new SendSmsLog();
        try {
            String dateFormat = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
            String authorization = smsConfig.getAccountSid() + ":" + dateFormat;
            String auth = Base64.getEncoder().encodeToString(authorization.getBytes());
            String sig = smsConfig.getAccountSid() + smsConfig.getAuthToken() + dateFormat;
            String sigParameter = DigestUtils.md5Hex(sig.getBytes()).toUpperCase();
            SmsFeignRequest smsFeignRequest = new SmsFeignRequest();
            smsFeignRequest.setAppId(smsConfig.getAppId());
            smsFeignRequest.setTemplateId(smsConfig.getTemplateId());
            smsFeignRequest.setTo(mobile);
            smsFeignRequest.setDatas(new String[]{smsCode, String.valueOf(smsConfig.getVaildate() / 60000)});
            SmsFeignResponse response = smsFeignClient.sendSms(auth, smsConfig.getAccountSid(), sigParameter, smsFeignRequest);
            Logger.info(this, "打印返回参数：" + response);
            sendSmsLog.setReqParm(JsonUtil.toJsonString(smsFeignRequest));
            sendSmsLog.setRespBody(JsonUtil.toJsonString(response));
            sendSmsLog.setRespStatus(response.getStatusCode());
            sendSmsLog.setSmsMobile(mobile);
            sendSmsLog.setSmsUuid(smsUuid);
            // 不相等，抛出对应的错误码
            if (!ErrorCode.SUUCCESS.equals(response.getStatusCode())) {
                throw new VailDataException(Error.SMS_EXCEPTION, response.getStatusMsg());
            }
        } finally {
            sendSmsLogMapper.insertSelective(sendSmsLog);
        }

    }


    /**
     * 验证码校验
     *
     * @param smsRequest
     * @return
     */
    public SmsInfo checkSms(SmsCheckRequest smsRequest) {

        Sms sms = smsMapper.findSmsBySmsUuid(smsRequest.getSmsUuid());
        // 短信验证码校验
        vailSmsCheck(sms, smsRequest);
        //假如验证码不相等，校验失败
        if (!smsRequest.getSmsCode().equals(sms.getSmsCode())) {
            //增加失败次数
            int row = smsMapper.updateSmsRetryBySmsUuid(smsRequest.getSmsUuid());
            Logger.info(this, "修改校验次数是否成功：" + row);
            SmsInfo smsInfo = new SmsInfo();
            smsInfo.setRetry(sms.getRetry() - 1);
            smsInfo.setSmsUuid(sms.getSmsUuid());
            smsInfo.setVaildate(sms.getVaildate());
            return smsInfo;
        }
        int row = smsMapper.updateSmsStatusBySmsUuid(smsRequest.getSmsUuid(), VAILDFLAG);
        Logger.info(this, "已经校验：" + row);
        UserInfo userInfo = new UserInfo();
        userInfo.setId(UserContext.getCurrentuserId().get());
        userInfo.setMobile(sms.getMobile());
        //验证成功，绑定手机号码
        int row1 = userInfoMapper.updateUserInfoById(userInfo);
        Logger.info(this, "绑定手机号码：" + row1);
        return null;
    }


    /**
     * 用户注册
     */
    public void register(UserRegisterRequest userRegisterRequest) {
        //校验短信是否验证
        Sms sms = smsMapper.findSmsBySmsUuidAndMobile(userRegisterRequest.getSmsUuid(), userRegisterRequest.getMobile());
        if (null == sms || !sms.getFlag().equals(VAILDFLAG)) {
            //已经校验过了
            Logger.info(this, "未通过短信校验");
            throw new SmsCheckException(Error.NOT_PASS_VAILD_SMS_CODE, "");
        }
        //判断是否已经注册
        UserInfo user = userInfoMapper.findUserInfoByMobile(userRegisterRequest.getMobile());
        if (null != user) {
            // 该用户已经注册
            Logger.info(this, "该拥有已经注册");
            throw new UserInfoException(Error.ALREADY_REGISTER, userRegisterRequest.getMobile());
        }
        // 拼装用户信息
        UserInfo userInfo = new UserInfo();
        userInfo.setId(IdUtils.getId(userRegisterRequest.getCity()));
        BeanUtils.copyProperties(userRegisterRequest, userInfo);
        userInfo.setCoor(getCoor());
        userInfo.setcTime(new Date());
        userInfo.setuTime(new Date());
        userInfo.setStar(3);
        int row = userInfoMapper.insertSelective(userInfo);
        Logger.info(this, "注册用户是否成功：" + row);
        //更改短信注册状态
        int row2 = smsMapper.updateSmsStatusBySmsUuid(userRegisterRequest.getSmsUuid(), VAILDFLAG + 1);
        Logger.info(this, "短信注册：" + row2);
    }

    /**
     * 用户登录
     *
     * @param userLoginRequest
     */
    public Token login(UserLoginRequest userLoginRequest) {

        UserInfo userInfo = userInfoMapper.findUserInfoByMobileAndPwd(userLoginRequest.getMobile(), userLoginRequest.getPwd());
        // 查询不到用户
        if (null == userInfo) {
            // 错误用户名或密码
            Logger.info(this, "错误的用户名或密码");
            throw new UserInfoException(Error.NOT_EXIST_USER, "");
        }
        // 设置token 信息
        UserInfoToken userInfoToken = userInfoTokenMapper.findUserInfoTokenById(userInfo.getId());
        if (null == userInfoToken) {
            userInfoToken = new UserInfoToken();
            userInfoToken.setId(userInfo.getId());
            userInfoToken.setToken(UUIDUtils.getInstance().getUniqueId());
            userInfoToken.setExpireTime(System.currentTimeMillis() + tokenConfig.getExpireTime());
            int row = userInfoTokenMapper.insertSelective(userInfoToken);
            Logger.info(this, "插入token信息 成功：" + row);
        } else {
            //存在更新数据
            userInfoToken.setToken(UUIDUtils.getInstance().getUniqueId());
            userInfoToken.setExpireTime(System.currentTimeMillis() + tokenConfig.getExpireTime());
            int row = userInfoTokenMapper.updateUserInfoToken(userInfoToken);
            Logger.info(this, "更新token信息 成功：" + row);
        }


        //设置登录用户信息
        Token token = new Token();
        BeanUtils.copyProperties(userInfoToken, token);
        BeanUtils.copyProperties(userInfo, token);
        return token;
    }

    /**
     * 获取个人信息
     *
     * @return
     */
    public UserInfoDetail userInfo() {
        Logger.debug(this, "查询用户详情");
        String userId = UserContext.getCurrentuserId().get();
        UserInfoDetail userInfo = userInfoMapper.findUserInfoDetailById(userId);

        // 查询不到用户
        if (null == userInfo) {
            // 错误用户名或密码
            Logger.info(this, "不存在的用户");
            throw new UserInfoException(Error.NOT_EXIST_USER, "");
        }

        //增加获取我要借和向我借的数量
        BorrowTimesDto borrowTimesDto = transInfoMapper.findLendTimesByUserId(userId);
        if (null != borrowTimesDto) {
            userInfo.setToLends(borrowTimesDto.getToLends());
        }

        BorrowTimesDto borrowTimesDto2 = transInfoMapper.findBorrowTimesByUserId(userId);
        if (null != borrowTimesDto2) {
            userInfo.setToBorrows(borrowTimesDto2.getToBorrows());
        }
        return userInfo;
    }

    /**
     * 更新用户坐标
     *
     * @return
     */
    public void coor(String coor) {
        Logger.debug(this, "查询用户详情");
        UserInfo userInfo = userInfoMapper.findUserInfoById(UserContext.getCurrentuserId().get());
        if (null == userInfo) {
            //查询用户信息为空
            Logger.info(this, "用户信息为空");
            throw new NotExistUserException(UserContext.getCurrentuserId().get());
        }
        UserInfo info = new UserInfo();
        info.setId(UserContext.getCurrentuserId().get());
        info.setCoor(coor);
        Integer row = userInfoMapper.updateUserInfoById(info);
        Logger.info(this, "更新用户坐标成功 ：" + row);
    }

    /**
     * 获取当前用户坐标
     *
     * @return
     */
    public String getCoor() {
        UserInfo userInfo = userInfoMapper.findUserInfoById(UserContext.getCurrentuserId().get());
        if (null == userInfo || null == userInfo.getCoor()) {
            // 返回一个默认值
            return "1,1";
        }
        return userInfo.getCoor();

    }


    /**
     * 获取当前用户坐标
     *
     * @return
     */
    public String getCity() {
        UserInfo userInfo = userInfoMapper.findUserInfoById(UserContext.getCurrentuserId().get());
        if (null == userInfo || null == userInfo.getCity()) {
            // 返回一个默认值
            return "";
        }
        return userInfo.getCity();

    }

    /**
     * 校验发送短信时间
     *
     * @param smsRequest
     */
    private void vailSms(SmsRequest smsRequest) {
        List<Sms> smsList = smsMapper.findSmsByMobileAndInVaildate(smsRequest.getMobile(), new Date(System.currentTimeMillis()));
        // 在规定时间内，不能重复获取验证码
        if (CollectionUtils.isNotEmpty(smsList)) {
            Logger.info(this, "规定时间内不能重复发送");
            throw new VailDataException(Error.USER_SMS_IN_TIME, String.valueOf(smsConfig.getVaildate() / 1000));
        }
    }

    /**
     * 短信验证码校验
     *
     * @param sms
     */
    private void vailSmsCheck(Sms sms, SmsCheckRequest smsRequest) {
        // 假如不存在，校验失败
        if (null == sms) {
            Logger.info(this, "不存在验证码");
            throw new SmsCheckException(Error.NOT_EXIST_SMS_UUID, smsRequest.getSmsUuid());
        }
        // 超过验证次数
        if (sms.getRetry() <= 0) {
            Logger.info(this, "超过验证次数");
            throw new SmsCheckException(Error.OUT_SMS_CODE, String.valueOf(sms.getRetry()));
        }
        // 超过验证时间
        if (sms.getVaildate().compareTo(new Date(System.currentTimeMillis())) <= 0) {
            Logger.info(this, "超过验证时间");
            throw new SmsCheckException(Error.OUT_SMS_CODE_TIME, DateUtil.dateToStr(sms.getVaildate(), "yyyy-MM-dd HH:mm:ss"));
        }

        if (!sms.getFlag().equals(VAILDFLAG - 1)) {
            //已经校验过了
            Logger.info(this, "校验码已经被校验");
            throw new SmsCheckException(Error.OUT_VAILD_SMS_CODE, String.valueOf(sms.getVaildate()));
        }
    }


    //随机验证码
    private String randomNumber(Integer length) {
        StringBuffer sb = new StringBuffer();
        Random r = new Random(System.nanoTime());
        for (int i = 0; i < length; i++) {
            sb.append(r.nextInt(10) + "");
        }
        return sb.toString();
    }
}
